﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using KinectAPI;
using KinectAPI.Gestures.NDollar;
using Microsoft.Win32;
using System.Xml;
using xn;

namespace NDollarKinectPlayer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private BodyTracker _skeleton;

        bool playerMode;

        // Pour un New
        Dictionary<string, List<PointR>> _user;
        bool Record = false;
        private GeometricRecognizer _rec;
        string OpenFileName;

        // Pour un Open
        List<List<PointR>> strokes = new List<List<PointR>>();

        public MainWindow()
        {
            InitializeComponent();
        }

        #region Menu Handler

        private void New_Click(object sender, RoutedEventArgs e)
        {
            _skeleton = new BodyTracker();
            _skeleton.UsersUpdated += new BodyTracker.UsersUpdatedHandler(Users_Updated);

            _user = new Dictionary<string, List<PointR>>();
            _user.Add("Head", new List<PointR>());
            _user.Add("Neck", new List<PointR>());
            _user.Add("LeftShoulder", new List<PointR>());
            _user.Add("LeftElbow", new List<PointR>());
            _user.Add("LeftHand", new List<PointR>());
            _user.Add("RightShoulder", new List<PointR>());
            _user.Add("RightElbow", new List<PointR>());
            _user.Add("RightHand", new List<PointR>());
            _user.Add("Torso", new List<PointR>());
            _user.Add("LeftHip", new List<PointR>());
            _user.Add("LeftKnee", new List<PointR>());
            _user.Add("LeftFoot", new List<PointR>());
            _user.Add("RightHip", new List<PointR>());
            _user.Add("RightKnee", new List<PointR>());
            _user.Add("RightFoot", new List<PointR>());

            playerMode = false;
        }

        private void Open_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Gestures (*.xml)|*.xml";
            ofd.Title = "Open Gesture";
            ofd.RestoreDirectory = true;
            ofd.ShowDialog(this);
            strokes.Clear();
            OpenFileName = ofd.FileName;
            GestureReader(ofd.FileName);

            slider1.Maximum = strokes.ElementAt(0).Count - 1;
            slider1.SelectionStart = 0;
            slider1.SelectionEnd = strokes.ElementAt(0).Count - 1;
            slider2.Maximum = strokes.ElementAt(0).Count - 1;
            slider3.Maximum = strokes.ElementAt(0).Count - 1;
            slider3.Value = slider3.Maximum;
            label3.Content = slider1.Maximum + 1;
            ShowSkeleton((int)slider1.Minimum);

            playerMode = true;
        }

        private void Save_Click(object sender, RoutedEventArgs e)
        {
            if (playerMode)
            {
                List<List<PointR>> _strokes = new List<List<PointR>>();
                List<int> _numPtsInStroke = new List<int>();
                foreach (List<PointR> points in strokes)
                {
                    List<PointR> _points = new List<PointR>();
                    int i = 0;
                    foreach (PointR point in points)
                    {
                        if (slider2.Value < i && i < slider3.Value)
                        {
                            _points.Add(point);
                        }
                        i++;
                    }
                    _numPtsInStroke.Add(_points.Count);
                    _strokes.Add(_points);
                }

                _rec = new GeometricRecognizer();
                _rec.SaveGesture(OpenFileName, _strokes, _numPtsInStroke);
            }
            else
            {
                SaveAs_Click(sender, e);
            }
        }

        private void SaveAs_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "Gestures (*.xml)|*.xml";
            dlg.Title = "Save Gesture As";
            dlg.AddExtension = true;
            dlg.RestoreDirectory = true;

            List<List<PointR>> _strokes = new List<List<PointR>>();
            List<int> _numPtsInStroke = new List<int>();
            if (playerMode)
            {
                foreach (List<PointR> points in strokes)
                {
                    List<PointR> _points = new List<PointR>();
                    int i = 0;
                    foreach (PointR point in points)
                    {
                        if (slider2.Value < i && i < slider3.Value)
                        {
                            _points.Add(point);
                        }
                        i++;
                    }
                    _numPtsInStroke.Add(_points.Count);
                    _strokes.Add(_points);
                }
            }
            else
            {
                foreach (KeyValuePair<string, List<PointR>> pair in _user)
                {
                    _numPtsInStroke.Add(pair.Value.Count);
                    _strokes.Add(pair.Value);
                }
            }

            dlg.ShowDialog(this);
            _rec = new GeometricRecognizer();
            _rec.SaveGesture(dlg.FileName, _strokes, _numPtsInStroke);
        }

        private void Exit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        #endregion

        #region Buttons Handler

        #region Menu New Context

        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            _rec = new GeometricRecognizer();
            Record = true;
        }

        private void buttonStop_Click(object sender, RoutedEventArgs e)
        {
            Record = false;
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "Gestures (*.xml)|*.xml";
            dlg.Title = "Save Gesture As";
            dlg.AddExtension = true;
            dlg.RestoreDirectory = true;
            List<List<PointR>> _strokes = new List<List<PointR>>();
            List<int> _numPtsInStroke = new List<int>();
            foreach (KeyValuePair<string, List<PointR>> pair in _user)
            {
                _numPtsInStroke.Add(pair.Value.Count);
                _strokes.Add(pair.Value);
            }

            dlg.ShowDialog(this);
            _rec.SaveGesture(dlg.FileName, _strokes, _numPtsInStroke);
        }

        #endregion

        #region Menu Open Context

        private void buttonNext_Click(object sender, RoutedEventArgs e)
        {
            slider1.Value++;
            label1.Content = slider1.Value + 1;
            ShowSkeleton((int)slider1.Value);
        }

        private void buttonPreview_Click(object sender, RoutedEventArgs e)
        {
            slider1.Value--;
            label1.Content = slider1.Value + 1;
            ShowSkeleton((int)slider1.Value);
        }

        private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (slider1.Value < slider2.Value) slider1.Value = slider2.Value;
            if (slider3.Value < slider1.Value) slider1.Value = slider3.Value;
            label1.Content = slider1.Value + 1;
            ShowSkeleton((int)slider1.Value);
        }

        private void slider2_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (slider3.Value < slider2.Value) slider2.Value = slider3.Value;
            if (slider1.Value < slider2.Value) slider1.Value = slider2.Value;
            slider1.SelectionStart = slider2.Value;
        }

        private void slider3_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (slider3.Value < slider2.Value) slider3.Value = slider2.Value;
            if (slider3.Value < slider1.Value) slider1.Value = slider3.Value;
            slider1.SelectionEnd = slider3.Value;
        }

        #endregion

        #endregion

        void Users_Updated(object sender, UsersEventArgs e)
        {
            Dispatcher.BeginInvoke((Action)delegate
            {
                lock (this)
                {
                    LayoutRoot1.Children.Clear();
                    foreach (var user in e.users)
                    {
                        #region Add the new points

                        if (Record)
                        {
                            _user["Head"].Add(new PointR(user.Head.X, user.Head.Y, Environment.TickCount));
                            _user["Neck"].Add(new PointR(user.Neck.X, user.Neck.Y, Environment.TickCount));
                            _user["LeftShoulder"].Add(new PointR(user.LeftShoulder.X, user.LeftShoulder.Y, Environment.TickCount));
                            _user["LeftElbow"].Add(new PointR(user.LeftElbow.X, user.LeftElbow.Y, Environment.TickCount));
                            _user["LeftHand"].Add(new PointR(user.LeftHand.X, user.LeftHand.Y, Environment.TickCount));
                            _user["RightShoulder"].Add(new PointR(user.RightShoulder.X, user.RightShoulder.Y, Environment.TickCount));
                            _user["RightElbow"].Add(new PointR(user.RightElbow.X, user.RightElbow.Y, Environment.TickCount));
                            _user["RightHand"].Add(new PointR(user.RightHand.X, user.RightHand.Y, Environment.TickCount));
                            _user["Torso"].Add(new PointR(user.Torso.X, user.Torso.Y, Environment.TickCount));
                            _user["LeftHip"].Add(new PointR(user.LeftHip.X, user.LeftHip.Y, Environment.TickCount));
                            _user["LeftKnee"].Add(new PointR(user.LeftKnee.X, user.LeftKnee.Y, Environment.TickCount));
                            _user["LeftFoot"].Add(new PointR(user.LeftFoot.X, user.LeftFoot.Y, Environment.TickCount));
                            _user["RightHip"].Add(new PointR(user.RightHip.X, user.RightHip.Y, Environment.TickCount));
                            _user["RightKnee"].Add(new PointR(user.RightKnee.X, user.RightKnee.Y, Environment.TickCount));
                            _user["RightFoot"].Add(new PointR(user.RightFoot.X, user.RightFoot.Y, Environment.TickCount));
                        }

                        #endregion

                        DrawSkeleton(user);
                    }
                }
            });
        }

        #region Drawings Methods

        void DrawCircle(float positionX, float positionY, float positionZ, Color color, Canvas layout)
        {
            float diametre = 10;

            System.Windows.Shapes.Ellipse ellipse = new System.Windows.Shapes.Ellipse
            {
                Fill = new SolidColorBrush(color),
                Width = diametre,
                Height = diametre,
                Margin = new Thickness(positionX - (diametre / 2), positionY - (diametre / 2), 0, 0)
            };

            layout.Children.Add(ellipse);
        }

        void DrawLine(float positionX1, float positionY1, float positionX2, float positionY2, Color color, Canvas layout)
        {
            Line line = new Line
            {
                X1 = positionX1,
                Y1 = positionY1,
                X2 = positionX2,
                Y2 = positionY2,
                Stroke = new SolidColorBrush(color),
                StrokeThickness = 3
            };
            layout.Children.Add(line);
        }

        void DrawSkeleton(UserSkeleton user)
        {
            Color userColor = Colors.Blue;

            #region Get body parts coordinates

            float headX = user.Head.X;
            float headY = user.Head.Y;
            float headZ = user.Head.Z;

            float neckX = user.Neck.X;
            float neckY = user.Neck.Y;
            float neckZ = user.Neck.Z;

            float leftShoulderX = user.LeftShoulder.X;
            float leftShoulderY = user.LeftShoulder.Y;
            float leftShoulderZ = user.LeftShoulder.Z;

            float leftElbowX = user.LeftElbow.X;
            float leftElbowY = user.LeftElbow.Y;
            float leftElbowZ = user.LeftElbow.Z;

            float leftHandX = user.LeftHand.X;
            float leftHandY = user.LeftHand.Y;
            float leftHandZ = user.LeftHand.Z;

            float rightShoulderX = user.RightShoulder.X;
            float rightShoulderY = user.RightShoulder.Y;
            float rightShoulderZ = user.RightShoulder.Z;

            float rightElbowX = user.RightElbow.X;
            float rightElbowY = user.RightElbow.Y;
            float rightElbowZ = user.RightElbow.Z;

            float rightHandX = user.RightHand.X;
            float rightHandY = user.RightHand.Y;
            float rightHandZ = user.RightHand.Z;

            float waistX = user.Torso.X;
            float waistY = user.Torso.Y;
            float waistZ = user.Torso.Z;

            float leftHipX = user.LeftHip.X;
            float leftHipY = user.LeftHip.Y;
            float leftHipZ = user.LeftHip.Z;

            float leftKneeX = user.LeftKnee.X;
            float leftKneeY = user.LeftKnee.Y;
            float leftKneeZ = user.LeftKnee.Z;

            float leftFootX = user.LeftFoot.X;
            float leftFootY = user.LeftFoot.Y;
            float leftFootZ = user.LeftFoot.Z;

            float rightHipX = user.RightHip.X;
            float rightHipY = user.RightHip.Y;
            float rightHipZ = user.RightHip.Z;

            float rightKneeX = user.RightKnee.X;
            float rightKneeY = user.RightKnee.Y;
            float rightKneeZ = user.RightKnee.Z;

            float rightFootX = user.RightFoot.X;
            float rightFootY = user.RightFoot.Y;
            float rightFootZ = user.RightFoot.Z;

            #endregion

            #region Draw skeleton points

            DrawCircle(headX, headY, headZ, userColor, LayoutRoot1);
            DrawCircle(neckX, neckY, neckZ, userColor, LayoutRoot1);
            DrawCircle(leftShoulderX, leftShoulderY, leftShoulderZ, userColor, LayoutRoot1);
            DrawCircle(leftElbowX, leftElbowY, leftElbowZ, userColor, LayoutRoot1);
            DrawCircle(leftHandX, leftHandY, leftHandZ, userColor, LayoutRoot1);
            DrawCircle(waistX, waistY, waistZ, userColor, LayoutRoot1);
            DrawCircle(rightShoulderX, rightShoulderY, rightShoulderZ, userColor, LayoutRoot1);
            DrawCircle(rightElbowX, rightElbowY, rightElbowZ, userColor, LayoutRoot1);
            DrawCircle(rightHandX, rightHandY, rightHandZ, userColor, LayoutRoot1);
            DrawCircle(leftHipX, leftHipY, leftHipZ, userColor, LayoutRoot1);
            DrawCircle(leftKneeX, leftKneeY, leftKneeZ, userColor, LayoutRoot1);
            DrawCircle(leftFootX, leftFootY, leftFootZ, userColor, LayoutRoot1);
            DrawCircle(rightHipX, rightHipY, rightHipZ, userColor, LayoutRoot1);
            DrawCircle(rightKneeX, rightKneeY, rightKneeZ, userColor, LayoutRoot1);
            DrawCircle(rightFootX, rightFootY, rightFootZ, userColor, LayoutRoot1);

            #endregion

            #region Draw skeleton line

            DrawLine(headX, headY, neckX, neckY, userColor, LayoutRoot1);
            DrawLine(neckX, neckY, leftShoulderX, leftShoulderY, userColor, LayoutRoot1);
            DrawLine(neckX, neckY, rightShoulderX, rightShoulderY, userColor, LayoutRoot1);
            DrawLine(leftShoulderX, leftShoulderY, leftElbowX, leftElbowY, userColor, LayoutRoot1);
            DrawLine(leftElbowX, leftElbowY, leftHandX, leftHandY, userColor, LayoutRoot1);
            DrawLine(neckX, neckY, waistX, waistY, userColor, LayoutRoot1);
            DrawLine(leftShoulderX, leftShoulderY, waistX, waistY, userColor, LayoutRoot1);
            DrawLine(rightShoulderX, rightShoulderY, waistX, waistY, userColor, LayoutRoot1);
            DrawLine(rightShoulderX, rightShoulderY, rightElbowX, rightElbowY, userColor, LayoutRoot1);
            DrawLine(rightElbowX, rightElbowY, rightHandX, rightHandY, userColor, LayoutRoot1);
            DrawLine(waistX, waistY, leftHipX, leftHipY, userColor, LayoutRoot1);
            DrawLine(waistX, waistY, rightHipX, rightHipY, userColor, LayoutRoot1);
            DrawLine(leftHipX, leftHipY, rightHipX, rightHipY, userColor, LayoutRoot1);
            DrawLine(leftHipX, leftHipY, leftKneeX, leftKneeY, userColor, LayoutRoot1);
            DrawLine(leftKneeX, leftKneeY, leftFootX, leftFootY, userColor, LayoutRoot1);
            DrawLine(rightHipX, rightHipY, rightKneeX, rightKneeY, userColor, LayoutRoot1);
            DrawLine(rightKneeX, rightKneeY, rightFootX, rightFootY, userColor, LayoutRoot1);

            #endregion
        }

        #endregion

        #region Gesture Reader

        private void GestureReader(string filename)
        {
            List<PointR> points = new List<PointR>();

            XmlTextReader reader = new XmlTextReader(filename);
            reader.WhitespaceHandling = WhitespaceHandling.None;
            reader.MoveToContent();

            reader.Read();
            reader.Read();
            while (reader.LocalName != "Gesture") // && (reader.NodeType != XmlNodeType.EndElement))
            {
                if (reader.LocalName == "Point")
                {
                    PointR p = PointR.Empty;
                    p.X = XmlConvert.ToDouble(reader.GetAttribute("X"));
                    p.Y = XmlConvert.ToDouble(reader.GetAttribute("Y"));
                    p.T = XmlConvert.ToInt32(reader.GetAttribute("T"));
                    points.Add(p);
                    reader.ReadStartElement("Point");
                }
                else if ((reader.LocalName == "Stroke") && (reader.NodeType != XmlNodeType.EndElement))
                {
                    // set up stroke index for the beginning of this stroke
                    //System.Console.WriteLine("Add new stroke");
                    strokes.Add(new List<PointR>(points));
                    points = new List<PointR>();
                    reader.Read();
                }
                else
                {
                    reader.Read(); // read start Stroke tags and do nothing
                }
            }
            // add last stroke size
            strokes.Add(new List<PointR>(points));
        }

        private void ShowSkeleton(int num)
        {
            LayoutRoot1.Children.Clear();

            #region Read the Skeleton

            Dictionary<SkeletonJoint, SkeletonJointPosition> user = new Dictionary<SkeletonJoint, SkeletonJointPosition>();
            SkeletonJointPosition pos = new SkeletonJointPosition();
            pos.position = new Point3D((float)strokes.ElementAt(0).ElementAt(num).X, (float)strokes.ElementAt(0).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.Head, pos);

            pos.position = new Point3D((float)strokes.ElementAt(1).ElementAt(num).X, (float)strokes.ElementAt(1).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.Neck, pos);

            pos.position = new Point3D((float)strokes.ElementAt(2).ElementAt(num).X, (float)strokes.ElementAt(2).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.LeftShoulder, pos);

            pos.position = new Point3D((float)strokes.ElementAt(3).ElementAt(num).X, (float)strokes.ElementAt(3).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.LeftElbow, pos);

            pos.position = new Point3D((float)strokes.ElementAt(4).ElementAt(num).X, (float)strokes.ElementAt(4).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.LeftHand, pos);

            pos.position = new Point3D((float)strokes.ElementAt(5).ElementAt(num).X, (float)strokes.ElementAt(5).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.RightShoulder, pos);

            pos.position = new Point3D((float)strokes.ElementAt(6).ElementAt(num).X, (float)strokes.ElementAt(6).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.RightElbow, pos);

            pos.position = new Point3D((float)strokes.ElementAt(7).ElementAt(num).X, (float)strokes.ElementAt(7).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.RightHand, pos);

            pos.position = new Point3D((float)strokes.ElementAt(8).ElementAt(num).X, (float)strokes.ElementAt(8).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.Torso, pos);

            pos.position = new Point3D((float)strokes.ElementAt(9).ElementAt(num).X, (float)strokes.ElementAt(9).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.LeftHip, pos);

            pos.position = new Point3D((float)strokes.ElementAt(10).ElementAt(num).X, (float)strokes.ElementAt(10).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.LeftKnee, pos);

            pos.position = new Point3D((float)strokes.ElementAt(11).ElementAt(num).X, (float)strokes.ElementAt(11).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.LeftFoot, pos);

            pos.position = new Point3D((float)strokes.ElementAt(12).ElementAt(num).X, (float)strokes.ElementAt(12).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.RightHip, pos);

            pos.position = new Point3D((float)strokes.ElementAt(13).ElementAt(num).X, (float)strokes.ElementAt(13).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.RightKnee, pos);

            pos.position = new Point3D((float)strokes.ElementAt(14).ElementAt(num).X, (float)strokes.ElementAt(14).ElementAt(num).Y, (float)0);
            user.Add(SkeletonJoint.RightFoot, pos);

            UserSkeleton userSkel = new UserSkeleton(0, user);

            #endregion

            DrawSkeleton(userSkel);
        }

        #endregion

    }
}
